package cn.file.utils;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.StringJoiner;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author ZhuHJay
 * @date 2022/3/17 19:47
 */
public class FileUtils {

    private static String[] getLogData(File logFile) throws Exception{
        if( logFile.exists() ){
            BufferedReader bf = new BufferedReader(new FileReader(logFile));
            String data = bf.readLine();
            bf.close();
            return data.split(",");
        }
        return null;
    }

    public static void transfer(File src, File target, int threadNum) throws Exception{
        if( !src.exists() ){
            throw new IOException("资源路径不存在");
        }
        if( threadNum <= 0 ){
            throw new RuntimeException("线程数定义有误");
        }
        // 获取一个存储断点的日志文件
        File logFile = new File("G:\\IDM_DownLoad\\download.log");
        // 定义一个存储断点的map
        final Map<Integer, Long> logMap = new ConcurrentHashMap<>(threadNum);
        final String[] diskLogData = getLogData(logFile);

        // 计算每个线程所需要运行复制的字节数
        long allotByteSize = (long) Math.ceil(src.length() / (double)threadNum);
        List<Thread> threadList = new ArrayList<>();

        for (int i = 0; i < threadNum; i++) {
            final int threadIndex = i;
            Thread t = new Thread(() -> {
                // 记录该线程读写的起始位置
                long startIndex = diskLogData == null ?
                        threadIndex * allotByteSize : Long.parseLong(diskLogData[threadIndex]);
                // 记录该线程当前已写入的数量
                long len = -1,
                     flushWriteSize = startIndex - threadIndex * allotByteSize;
                // 字节缓存数组
                byte[] bytes = new byte[1024 * 8];
                RandomAccessFile logRaf = null;
                RandomAccessFile inputRaf = null;
                RandomAccessFile outputRaf = null;
                try {
                    inputRaf = new RandomAccessFile(src, "r");
                    outputRaf = new RandomAccessFile(target, "rw");
                    logRaf = new RandomAccessFile(logFile, "rw");
                    // 将指针移动到对应的地方
                    inputRaf.seek(startIndex);
                    outputRaf.seek(startIndex);

                    // 开始进行内容的复制
                    while (true){
                        // 获取需要复制的内容
                        len = inputRaf.read(bytes);

                        // 判断: 如果len == -1, 那么就是获取结束了
                        if( len == -1 ){
                            break;
                        }

                        // 将内容读写到对应的位置
                        outputRaf.write(bytes);
                        flushWriteSize += len;

                        // 记录日志, 持久化此时的断点位置
                        logMap.put(threadIndex, startIndex + flushWriteSize);
                        StringJoiner joiner = new StringJoiner(",");
                        logMap.forEach((key, value) -> joiner.add(String.valueOf(value)));
                        logRaf.seek(0);
                        logRaf.write(joiner.toString().getBytes(StandardCharsets.UTF_8));

                        // 判断: 如果当前写入的量已经大于需要写入的量, 那么就退出
                        if( flushWriteSize >= allotByteSize ){
                            break;
                        }
                    }
                    System.out.println("线程" + threadIndex + "执行结束！");

                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    try {
                        if( logRaf != null ){
                            logRaf.close();
                        }
                        if( inputRaf != null ){
                            inputRaf.close();
                        }
                        if( outputRaf != null ){
                            outputRaf.close();
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
            t.start();
            threadList.add(t);
        }
        for (Thread thread : threadList) {
            thread.join();
        }
        // 此时文件传输完成, 删除日志
        if( !logFile.delete() ){
            throw new IOException("日志文件删除失败");
        }
    }

}
